﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Threading;
using MugenInjection.Attributes;
using MugenInjection.Core.Components;
using MugenInjection.Delegates;
using MugenInjection.Exceptions;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;
using MugenInjection.Interface.Behaviors;
using MugenInjection.Interface.Components;

namespace MugenInjection.Core
{
    /// <summary>
    /// The base implementation of an <see cref="IInjector"/>.
    /// </summary>
    public abstract class InjectorBase : DisposableObject, IInjector, IEquatable<InjectorBase>
    {
        #region Fields

        private readonly BindingCollection _bindingCollection;
        private readonly List<IInjector> _childInjectors;
        private readonly IComponentContainer _componentContainer;
        private readonly List<IInjectorModule> _injectorModules;
        private readonly IInjectorSettings _settings;
        private IInjector _parent;
        private readonly int _id;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="InjectorBase" /> class.
        /// </summary>
        /// <param name="parent">The specified parent <see cref="IInjector"/>.</param>
        /// <param name="settings">The specified <see cref="IInjectorSettings"/> for current <see cref="IInjector"/>.</param>
        /// <param name="componentContainerContainer">The specified <see cref="IComponentContainer"/>. </param>
        /// <param name="components">The specified <see cref="IComponent"/>(s). </param>
        protected InjectorBase(IInjector parent, IInjectorSettings settings, IComponentContainer componentContainerContainer,
                               IEnumerable<IComponent> components)
        {
            Validate.ArgumentNotNull(settings, "settings");
            Validate.ArgumentNotNull(componentContainerContainer, "componentContainerContainer");
            _id = InjectorUtils.GetNextId();
            if (parent != null)
                // ReSharper disable DoNotCallOverridableMethodsInConstructor
                SetParent(parent);
            // ReSharper restore DoNotCallOverridableMethodsInConstructor
            _childInjectors = new List<IInjector>();
            _injectorModules = new List<IInjectorModule>();
            _settings = settings;
            _bindingCollection = new BindingCollection();
            Modules = new ReadOnlyCollection<IInjectorModule>(_injectorModules);
            ChildInjectors = new ReadOnlyCollection<IInjector>(_childInjectors);
            _componentContainer = componentContainerContainer;
            componentContainerContainer.Initialize(this);
            if (components == null)
                components = new IComponent[0];
            LoadComponents(components);
        }

        #endregion

        #region Private method

        /// <summary>
        /// Loads the specified <see cref="IComponent"/>s in the current <see cref="IInjector"/>.
        /// </summary>
        /// <param name="components">The specifeid <see cref="IComponent"/>s.</param>
        private void LoadComponents(IEnumerable<IComponent> components)
        {
            IModuleManagerComponent moduleManagerComponent = null;
            foreach (IComponent component in components)
            {
                var managerComponent = component as IModuleManagerComponent;
                if (managerComponent != null)
                {
                    if (moduleManagerComponent != null)
                        throw new ArgumentException("Component of the type IModuleManagerComponent is already registered. This component can be only one.");
                    moduleManagerComponent = managerComponent;
                    continue;
                }
                _componentContainer.Add(component);
            }
            if (!_componentContainer.IsExist<IBehaviorManagerComponent>())
                _componentContainer.Add(new BehaviorManagerComponent());

            //last load the module manager component.
            if (moduleManagerComponent == null)
                moduleManagerComponent = new ModuleManagerComponent();
#if SCANASSEMBLY
            moduleManagerComponent.AutoScanAssembly = _settings.IsAutoScanAssembly;
#endif
            _componentContainer.Add(moduleManagerComponent);
        }

        /// <summary>
        /// Tries to resolve the specified <see cref="IBindingContext"/> using custom behavior.
        /// </summary>
        private static object TryResolve(IBindingContext bindingContext, Func<object, object> converter)
        {
            var componentContainer = bindingContext.CallInjector.Components;
            var settings = bindingContext.CallInjector.Settings;

            //If type is self bindable
            IBinding selfBindable = componentContainer
                .BindingManagerComponent
                .CreateSelfBindable(bindingContext.Service, settings.DefaultActivatorFactory, settings.DefaultConstructorResolverFactory, true);
            if (selfBindable != null)
                return Activate(selfBindable, bindingContext, converter);

            if (bindingContext.ParameterInfo != null && bindingContext.ParameterInfo.IsDefined(typeof(ParamArrayAttribute), true))
                return Activate(Array.CreateInstance(bindingContext.ParameterInfo.ParameterType.GetElementType(), 0), converter);

            object result;
            IBehaviorManagerComponent behaviorManager;
            IResolveUnregisteredTypeBehavior resolveUnregisteredTypeBehavior;
            if (componentContainer.TryGet(out behaviorManager)
                && behaviorManager.TryGet(out resolveUnregisteredTypeBehavior)
                && resolveUnregisteredTypeBehavior.Resolve(bindingContext, out result))
            {
                if (converter != null)
                    result = converter(result);
                return result;
            }
            throw new BindingNotFoundException(bindingContext);
        }

        private static object Activate(IBinding binding, IBindingContext bindingContext, Func<object, object> converter)
        {
            var injector = bindingContext.Injector;
            object result = injector.Components.BindingActivatorComponent.Activate(binding, bindingContext);
            if (converter != null)
                result = converter(result);
            if (injector.Settings.ThrowErrorForNullableBinding && result == null)
                throw new NullableBindingException(bindingContext);
            return result;
        }

        private static object Activate(object value, Func<object, object> converter)
        {
            if (converter == null)
                return value;
            return converter(value);
        }

        /// <summary>
        /// Determines whether the specified request can be resolved.
        /// </summary>
        /// <param name="bindingContext"> The specified <see cref="IBindingContext"/>.</param>
        /// <param name="findInParent">Find in parent bindings.</param>
        /// <returns>An instance of <see cref="IBinding"/> not null if the specified service has been resolved</returns>
        private static IBinding TryFindBinding(IBindingContext bindingContext, bool findInParent)
        {
            IInjector injector = bindingContext.CallInjector;
            var settings = bindingContext.CallInjector.Settings;
            do
            {
                var priorityBinding = injector.Components.BindingManagerComponent.FindBinding(bindingContext);
                if (priorityBinding != null)
                    return priorityBinding;

                if (findInParent)
                {
                    injector = injector.GetParent();
                    if (settings.UseParentToResolve && injector != null)
                        bindingContext.Update(bindingContext.CallInjector, injector);
                }
                else
                    injector = null;
            } while (injector != null);
            return null;
        }

        /// <summary>
        /// Finds the specified module.
        /// </summary>
        /// <param name="name">The specified module name.</param>
        /// <returns>An instance of <see cref="InjectorModule"/>.</returns>
        private IInjectorModule FindModule(string name)
        {
            foreach (var injectorModule in Modules)
                if (injectorModule.Name.Equals(name))
                    return injectorModule;
            return null;
        }

        /// <summary>
        /// Finds the specified module.
        /// </summary>
        /// <param name="type">The specified module <see cref="Type"/>.</param>
        /// <returns>An instance of <see cref="IInjectorModule"/>.</returns>
        private IInjectorModule FindModule(Type type)
        {
            foreach (var injectorModule in Modules)
                if (injectorModule.GetType() == type)
                    return injectorModule;
            return null;
        }

        /// <summary>
        /// Unloads the specified <see cref="InjectorModule"/>.
        /// </summary>
        /// <param name="injectorModule">The specified <see cref="InjectorModule"/>.</param>
        private void UnloadModuleInternal(IInjectorModule injectorModule)
        {
            injectorModule.Unload(this);
            _injectorModules.Remove(injectorModule);
        }

        private static object ActivateOneInstance(IBindingContext bindingContext, Func<object, object> converter)
        {
            IBinding binding = TryFindBinding(bindingContext, true);
            if (binding != null)
                return Activate(binding, bindingContext, converter);

            //use default parameter value if any.
            if (bindingContext.ParameterInfo != null && bindingContext.ParameterInfo.IsOptional)
                return Activate(bindingContext.ParameterInfo.DefaultValue, converter);

            //restore binding context
            bindingContext.Update(bindingContext.CallInjector, bindingContext.CallInjector);
            return TryResolve(bindingContext, converter);
        }

        private IEnumerable<IBinding> GetBindings(ActivateType activateType, IBindingContext bindingContext, bool useParent)
        {
            IInjector injector = this;
            do
            {
                if (activateType == ActivateType.AllUseBindingContext)
                {
                    foreach (IBinding b in injector.Components.BindingManagerComponent.FindAllBinding(bindingContext))
                        yield return b;
                }
                else
                {
                    foreach (IBinding b in injector.Components.BindingManagerComponent.FindAllBinding(bindingContext.Service))
                        yield return b;
                }

                //If use parent to get all objects.
                if (useParent)
                {
                    injector = injector.GetParent();
                    if (_settings.UseParentToResolve && injector != null)
                        bindingContext.Update(bindingContext.CallInjector, injector);
                }
                else
                    injector = null;
            } while (injector != null);
        }

        #endregion

        #region Methods

        protected virtual object Activate(IBindingContext bindingContext, ActivateType activateType, bool useParentForAll)
        {
            Type originalServiceType;
            Func<object, object> converterChain;
            List<object> listObject;
            var serviceType = _componentContainer.BindingActivatorComponent.GetServiceType(bindingContext,
                                                                                           out originalServiceType,
                                                                                           out converterChain);
            if (InjectorUtilsInternal.HasFlag(serviceType, ServiceType.Resolvable) && CanResolveInternal(bindingContext, true, false))
            {
                serviceType = ServiceType.Simple;
                converterChain = null;
            }
            
            //Trying to activate as a simple service
            if (serviceType == ServiceType.Simple || 
                (!InjectorUtilsInternal.HasFlag(serviceType, ServiceType.Activatable) && !InjectorUtilsInternal.HasFlag(serviceType, ServiceType.Complex)))
            {
                if (activateType == ActivateType.OneInstance)
                    return ActivateOneInstance(bindingContext, converterChain);

                listObject = new List<object>();
                foreach (var binding in GetBindings(activateType, bindingContext, useParentForAll))
                {
                    listObject.Add(Activate(binding, bindingContext, converterChain));
                }
                if (listObject.Count == 0)
                    return new[] { TryResolve(bindingContext, converterChain) };
                return listObject;
            }

            while (InjectorUtilsInternal.HasFlag(serviceType, ServiceType.Complex))
            {
                Func<object, object> convertAction;
                bindingContext.Service = originalServiceType;
                serviceType = _componentContainer.BindingActivatorComponent.GetServiceType(bindingContext,
                                                                                           out originalServiceType,
                                                                                           out convertAction);

                if (InjectorUtilsInternal.HasFlag(serviceType, ServiceType.Resolvable) 
                    && CanResolveInternal(bindingContext, true, false))
                {
                    serviceType = ServiceType.Simple;
                    convertAction = null;
                }
                if (convertAction != null)
                {
                    if (converterChain == null)
                        converterChain = convertAction;
                    else
                    {
                        var converter = converterChain;
                        converterChain = o =>
                        {
                            var converterContext = (ConverterContext)o;
                            var newContext = new ConverterContext(converterContext);
                            converterContext.Activator = context =>
                            {
                                newContext.BindingContext = context;
                                return convertAction(newContext);
                            };
                            return converter(converterContext);
                        };
                    }
                }

            }

            if (converterChain == null)
                converterChain = o =>
                                     {
                                         var converterContext = (ConverterContext)o;
                                         return converterContext.Activator(converterContext.BindingContext);
                                     };

            if (activateType == ActivateType.OneInstance)
                return Activate(new ConverterContext(bindingContext, context => ActivateOneInstance(context, null), null, false), converterChain);

            listObject = new List<object>();
            foreach (var binding in GetBindings(activateType, bindingContext, useParentForAll))
            {
                IBinding b = binding;
                var converterContext = new ConverterContext(new BindingContext(bindingContext), context => Activate(b, context, null), b, true);
                listObject.Add(Activate(converterContext, converterChain));
            }
            if (listObject.Count == 0)
                return new[]
                           {
                               Activate(new ConverterContext(bindingContext, context => TryResolve(context, null), null, true), converterChain)
                           };

            return listObject;
        }

        /// <summary>
        /// Determines whether the specified request can be resolved.
        /// </summary>
        /// <param name="bindingContext"> The specified <see cref="IBindingContext"/>.</param>
        /// <param name="searchInParent">If set to <c>true</c> search in parent bindings.</param>
        /// <param name="includeImplicitBindings">If set to <c>false</c> implicit bindings are ignored.</param>
        /// <returns><c>True</c> if the specified service has been resolved; otherwise, <c>false</c>.</returns>
        protected virtual bool CanResolveInternal(IBindingContext bindingContext, bool searchInParent, bool includeImplicitBindings)
        {
            if (includeImplicitBindings && InjectorUtilsInternal.IsSelfBindable(bindingContext.Service))
                return true;
            IInjector injector = this;
            do
            {
                using (var enumerator = injector.Components.BindingManagerComponent.FindAllBinding(bindingContext).GetEnumerator())
                    if (enumerator.MoveNext())
                        return true;
                if (searchInParent)
                    injector = injector.GetParent();
                else
                    injector = null;
            } while (injector != null);
            return false;
        }

        /// <summary>
        /// Creates a child <see cref="IInjector"/> for the current <see cref="IInjector"/>.
        /// </summary>
        /// <param name="settings">The specified settings for the child <see cref="IInjector"/>.</param>
        /// <param name="componentContainer">The specified <see cref="IComponentContainer"/>. </param>
        /// <param name="components">The specified <see cref="IComponent"/> for new <see cref="IInjector"/></param>
        /// <param name="modules">The specified <see cref="InjectorModule"/> for new <see cref="IInjector"/></param>
        /// <returns>An instacne of child <see cref="IInjector"/>.</returns>
        protected abstract IInjector CreateChildInjector(IInjectorSettings settings, IComponentContainer componentContainer,
                                             IList<IComponent> components, params IInjectorModule[] modules);
        #endregion

        #region Implementation of IInjectorResolver

        /// <summary>
        /// Creates the <see cref="IBindingContext"/> for the current <see cref="IInjectorResolver"/>.
        /// </summary>
        /// <param name="service">The specifed service <see cref="Type"/>.</param>
        /// <param name="member">The specified <see cref="MemberInfo"/> when injected specified service. Can be null.</param>
        /// <param name="parameterInfo">The specified <see cref="ParameterInfo"/> when injected specified service.</param>
        /// <param name="parameters">The specified parameters for service.</param>
        /// <param name="specialParameters">The special parameters for resolve specified service.</param>
        /// <returns>An instance of <see cref="IBindingContext"/>.</returns>
        public virtual IBindingContext CreateContext(Type service, MemberInfo member, ParameterInfo parameterInfo,
                                             IEnumerable<IInjectionParameter> parameters,
                                             IDictionary<string, object> specialParameters)
        {
            return new BindingContext(service, member, parameterInfo, this, this, InjectorUtilsInternal.GetParameters(parameters), specialParameters);
        }

        /// <summary>
        /// Injects values into properties, fields, methods in the specified object.
        /// </summary>
        /// <param name="target">The specified target for inject.</param>
        /// <param name="bindingContext">The specified <see cref="IBindingContext"/>.</param>
        public virtual void Inject<T>(ref T target, IBindingContext bindingContext)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(target, "target");
            Validate.ArgumentNotNull(bindingContext, "bindingContext");
            _componentContainer.BindingActivatorComponent.Inject(ref target, bindingContext);
        }


        /// <summary>
        /// Determines whether the specified request can be resolved.
        /// </summary>
        /// <param name="bindingContext"> The specified <see cref="IBindingContext"/>.</param>
        /// <param name="searchInParent">If set to <c>true</c> search in parent bindings.</param>
        /// <param name="includeImplicitBindings">If set to <c>false</c> implicit bindings are ignored.</param>
        /// <returns><c>True</c> if the specified service has been resolved; otherwise, <c>false</c>.</returns>
        public virtual bool CanResolve(IBindingContext bindingContext, bool searchInParent, bool includeImplicitBindings)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(bindingContext, "bindingContext");
            return CanResolveInternal(bindingContext, searchInParent, includeImplicitBindings);
        }

        /// <summary>
        /// Resolves an instance for the specified <see cref="IBindingContext"/>.
        /// </summary>
        /// <param name="bindingContext">The specified <see cref="IBindingContext"/>.</param>
        /// <returns>An instance of the service.</returns>
        public virtual object Resolve(IBindingContext bindingContext)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(bindingContext, "bindingContext");
            bindingContext.Update(this, this);
            return Activate(bindingContext, ActivateType.OneInstance, false);
        }

        /// <summary>
        /// Resolves all instances for the specified service <see cref="Type"/>.
        /// </summary>
        /// <param name="service">Service for resolve.</param>
        /// <param name="parameters">Parameters for service.</param>
        /// <param name="specialParameters">Special parameters for resolve specified service.</param>
        /// <param name="useParentToResolve">If <c>true</c> also use parent bindings to resolve.</param>
        /// <returns>A series of instances of the service.</returns>
        public virtual IList<object> ResolveAll(Type service, IEnumerable<IInjectionParameter> parameters,
                                        IDictionary<string, object> specialParameters, bool useParentToResolve)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(parameters, "parameters");
            var bindingContext = new BindingContext(service, null, null, this, this, InjectorUtilsInternal.GetParameters(parameters), specialParameters);
            return (IList<object>)Activate(bindingContext, ActivateType.AllUseType, useParentToResolve);
        }
        
        /// <summary>
        /// Resolves all instances for the specified <see cref="IBindingContext"/>.
        /// </summary>
        /// <param name="bindingContext">The specified <see cref="IBindingContext"/>.</param>
        /// <param name="useParentToResolve">If <c>true</c> also use parent bindings to resolve.</param>
        /// <returns>A series of instances of the service.</returns>
        public virtual IList<object> ResolveAll(IBindingContext bindingContext, bool useParentToResolve)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(bindingContext, "bindingContext");
            bindingContext.Update(this, this);
            return (IList<object>)Activate(bindingContext, ActivateType.AllUseBindingContext, useParentToResolve);
        }

        #endregion

        #region Implementation of IInjectorBinder

        /// <summary>
        /// Gets the collection of all bindings for the current <see cref="IInjectorBinder"/>.
        /// </summary>
        public IBindingCollection Bindings
        {
            get
            {
                return _bindingCollection;
            }
        }

        /// <summary>
        /// Creates the <see cref="IBindingContext"/> for the current <see cref="IInjectorBinder"/>.
        /// </summary>
        /// <param name="service">The specifed service <see cref="Type"/>.</param>
        /// <param name="member">The specified <see cref="MemberInfo"/> when injected specified service. Can be null.</param>
        /// <param name="parameterInfo">The specified <see cref="ParameterInfo"/> when injected specified service.</param>
        /// <param name="parameters">The specified parameters for service.</param>
        /// <param name="specialParameters">The special parameters for resolve specified service.</param>
        /// <returns>An instance of <see cref="IBindingContext"/>.</returns>
        public virtual IBindingContext CreateBinderContext(Type service, MemberInfo member, ParameterInfo parameterInfo, IEnumerable<IInjectionParameter> parameters, IDictionary<string, object> specialParameters)
        {
            return new BindingContext(service, member, parameterInfo, this, this, InjectorUtilsInternal.GetParameters(parameters), specialParameters);
        }

        /// <summary>
        /// Adds the specified <see cref="IBinding"/>.
        /// </summary>
        /// <param name="binding">The specified <see cref="IBinding"/>.</param>
        public virtual void AddBinding(IBinding binding)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(binding, "binding");
            _bindingCollection.AddBinding(binding);
        }

        /// <summary>
        /// Removes the specified <see cref="IBinding"/>.
        /// </summary>
        /// <param name="binding">The specified <see cref="IBinding"/>.</param>
        public virtual bool RemoveBinding(IBinding binding)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(binding, "binding");
            return _bindingCollection.RemoveBinding(binding);
        }

        /// <summary>
        /// Gets all binding for tne specified service <see cref="Type"/>.
        /// </summary>
        /// <param name="service">The specified service <see cref="Type"/>.</param>
        /// <returns>A series of bindings that are registered for the service.</returns>
        public virtual IEnumerable<IBinding> GetBindings(Type service)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(service, "service");
            return new ReadOnlyCollection<IBinding>(_componentContainer.BindingManagerComponent.FindAllBinding(service));
        }

        /// <summary>
        /// Gets all binding for the specified <see cref="IBindingContext"/>.
        /// </summary>
        /// <param name="bindingContext">The specified <see cref="IBindingContext"/>.</param>
        /// <returns>A series of bindings that are registered for the <see cref="IBindingContext"/>.</returns>
        public virtual IEnumerable<IBinding> GetBindings(IBindingContext bindingContext)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(bindingContext, "bindingContext");
            return _bindingCollection.WhereCanResolve(bindingContext);
        }

        #endregion

        #region Implementation of IInjector

        /// <summary>
        /// Gets the id of <see cref="IInjector"/>.
        /// </summary>
        public int Id
        {
            get { return _id; }
        }

        /// <summary>
        /// Gets the collection of all modules for current <see cref="IInjector"/>.
        /// </summary>
        public ReadOnlyCollection<IInjectorModule> Modules { get; private set; }

        /// <summary>
        /// Gets the collection of children <see cref="IInjector"/>s.
        /// </summary>
        public ReadOnlyCollection<IInjector> ChildInjectors { get; private set; }

        /// <summary>
        /// Gets the configuration options for current <see cref="IInjector"/>.
        /// </summary>
        public IInjectorSettings Settings
        {
            get
            {
                return _settings;
            }
        }

        /// <summary>
        /// Gets the collection of all components for current <see cref="IInjector"/>.
        /// </summary>
        public IComponentContainer Components
        {
            get
            {
                return _componentContainer;
            }
        }

        /// <summary>
        /// Gets the root <see cref="IInjector"/>.
        /// </summary>
        /// <returns></returns>
        public virtual IInjector GetRoot()
        {
            EnsureIsNotDisposed();
            return _parent == null ? this : _parent.GetRoot();
        }

        /// <summary>
        /// Gets the parent <see cref="IInjector"/>.
        /// </summary>
        /// <returns></returns>
        public virtual IInjector GetParent()
        {
            EnsureIsNotDisposed();
            return _parent;
        }

        /// <summary>
        /// Sets the specified <see cref="IInjector"/> as a parent, if the <see cref="IInjector"/> already has a parent will be thrown an <see cref="ArgumentException"/>.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        public virtual void SetParent(IInjector injector)
        {
            EnsureIsNotDisposed();
            if (injector == null)
            {
                if (_parent == null) return;
                var parent = _parent;
                _parent = null;
                parent.RemoveChild(this);
                return;
            }
            if (_parent != null && _parent != injector)
                throw new ArgumentException("The current IInjector already has a parent");
            if (_parent == injector) return;
            _parent = injector;
            injector.AddChild(this);
        }

        /// <summary>
        /// Creates a managed scope.
        /// </summary>
        /// <returns>An instance of <see cref="IManagedScope"/>.</returns>
        public virtual IManagedScope CreateManagedScope()
        {
            return new ManagedScope(this);
        }

        /// <summary>
        /// Creates a child <see cref="IInjector"/> for the current <see cref="IInjector"/>.
        /// </summary>
        /// <param name="settings">The specified settings for the child <see cref="IInjector"/>.</param>
        /// <param name="componentContainer">The specified <see cref="IComponentContainer"/>. </param>
        /// <param name="cloneComponents">The specifies whether to clone the components from the parent.</param>
        /// <param name="components">The specified <see cref="IComponent"/> for new <see cref="IInjector"/></param>
        /// <param name="modules">The specified <see cref="InjectorModule"/> for new <see cref="IInjector"/></param>
        /// <returns>An instance of <see cref="IInjector"/>.</returns>
        public virtual IInjector CreateChild(IInjectorSettings settings, IComponentContainer componentContainer,
                                     bool cloneComponents, IList<IComponent> components, params IInjectorModule[] modules)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(settings, "settings");
            Validate.ArgumentNotNull(componentContainer, "componentContainer");
            Validate.ArgumentNotNull(components, "components");
            var listComponents = new List<IComponent>(components);
            if (cloneComponents)
            {
                var clonedComponents = _componentContainer.CloneAll();
                for (int i = 0; i < clonedComponents.Count; i++)
                {
                    IComponent component = clonedComponents[i];
                    if (!component.IsSingle)
                    {
                        listComponents.Add(component);
                        continue;
                    }
                    bool isUniq = true;
                    foreach (var listComponent in listComponents)
                    {
                        if (listComponent.ComponentType != component.ComponentType) continue;
                        isUniq = false;
                        break;
                    }
                    if (isUniq)
                        listComponents.Add(component);
                }
            }
            var childInjector = CreateChildInjector(settings, componentContainer, listComponents, modules);
            if (cloneComponents)
            {
                componentContainer.BindingActivatorComponent = (IBindingActivatorComponent)_componentContainer.BindingActivatorComponent.Clone();
                componentContainer.BindingManagerComponent = (IBindingManagerComponent)_componentContainer.BindingManagerComponent.Clone();
            }
            AddChild(childInjector);
            return childInjector;
        }


        /// <summary>
        /// Adds the specified <see cref="IInjector"/> in the collection of <c>ChildInjectors</c>, if the <see cref="IInjector"/> already has a parent will be thrown an <see cref="ArgumentException"/>.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        public virtual void AddChild(IInjector injector)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(injector, "injector");
            var parent = injector.GetParent();
            if (parent != null && parent != this)
                throw new ArgumentException("The specified IInjector already has a parent");
            lock (_childInjectors)
            {
                if (!_childInjectors.Contains(injector))
                    _childInjectors.Add(injector);
            }
            if (parent == this) return;
            injector.SetParent(this);
        }

        /// <summary>
        /// Removes the child <see cref="IInjector"/>.
        /// </summary>
        /// <param name="injector">The specified <see cref="IInjector"/>.</param>
        public virtual void RemoveChild(IInjector injector)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(injector, "injector");
            lock (_childInjectors)
            {
                _childInjectors.Remove(injector);
            }
            injector.SetParent(null);
        }

        /// <summary>
        /// Determines whether a module with the specified name has been loaded in the <see cref="IInjector"/>.
        /// </summary>
        /// <param name="name">The specified module name.</param>
        /// <returns><c>True</c> if the specified module has been loaded; otherwise, <c>false</c>.</returns>
        public virtual bool ModuleIsLoaded(string name)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNullOrEmpty(name, "name");
            lock (_injectorModules)
            {
                return FindModule(name) != null;
            }
        }

        /// <summary>
        /// Determines whether a module with the specified <see cref="Type"/> has been loaded in the <see cref="IInjector"/>.
        /// </summary>
        /// <param name="moduleType">Module <see cref="Type"/>.</param>
        /// <returns><c>True</c> if the specified module has been loaded; otherwise, <c>false</c>.</returns>
        public virtual bool ModuleIsLoaded(Type moduleType)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(moduleType, "moduleType");
            lock (_injectorModules)
            {
                return FindModule(moduleType) != null;
            }
        }

        /// <summary>
        /// Creates a new instance of the module and loads it into the <see cref="IInjector"/>.
        /// </summary>
        /// <param name="moduleType">The type of the module.</param>
        public virtual void Load(Type moduleType)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(moduleType, "moduleType");
            Validate.IsAssignable<IInjectorModule>(moduleType);
            var module = (IInjectorModule)Resolve(new BindingContext(moduleType, null, null, this, this, InjectorUtilsInternal.EmptyInjectionParams, InjectorUtilsInternal.ReadOnlyEmptyDictionary));
            Load(module);
        }

        /// <summary>
        /// Loads the module into the <see cref="IInjector"/>.
        /// </summary>
        /// <param name="module">The specified <see cref="InjectorModule"/>.</param>
        public virtual void Load(IInjectorModule module)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(module, "module");
            lock (_injectorModules)
            {
                if (ModuleIsLoaded(module.Name))
                    throw new ArgumentException(string.Format("The module with the name of {0} an already loaded.", module.Name));
                module.Load(this);
                _injectorModules.Add(module);
            }
        }

        /// <summary>
        /// Unloads the module with the specified name.
        /// </summary>
        /// <param name="name">The specified module name.</param>
        public virtual void Unload(string name)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNullOrEmpty(name, "name");
            lock (_injectorModules)
            {
                IInjectorModule injectorModule = FindModule(name);
                if (injectorModule == null)
                    throw new KeyNotFoundException(string.Format("The module with the name of {0} was not found.", name));
                UnloadModuleInternal(injectorModule);
            }
        }

        /// <summary>
        /// Unloads the module with the specified <see cref="Type"/>.
        /// </summary>
        /// <param name="moduleType">The specified module <see cref="Type"/>.</param>
        public virtual void Unload(Type moduleType)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(moduleType, "moduleType");
            lock (_injectorModules)
            {
                IInjectorModule injectorModule = FindModule(moduleType);
                if (injectorModule == null)
                    throw new KeyNotFoundException(string.Format("The module with the type of {0} was not found.", InjectorUtilsInternal.FormatType(moduleType)));
                UnloadModuleInternal(injectorModule);
            }
        }

        /// <summary>
        /// Releases specified service with specified condition in scope.
        /// </summary>
        /// <param name="bindingContext">The specified <see cref="IBindingContext"/>.</param>
        /// <param name="needDispose">Try dispose object.</param>
        /// <returns><c>True</c> if the specified service has been release; otherwise, <c>false</c>.</returns>
        public virtual bool Release(IBindingContext bindingContext, bool needDispose)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(bindingContext, "bindingContext");
            bool result = false;
            foreach (IBinding binding in _componentContainer.BindingManagerComponent.FindAllBinding(bindingContext))
            {
                binding.ScopeLifecycle.ReleaseObjects(needDispose);
                result = true;
            }
            return result;
        }

        /// <summary>
        /// Releases all specified service in scope.
        /// </summary>
        /// <param name="service">The specified service <see cref="Type"/> for release.</param>
        /// <param name="needDispose">Try dispose object.</param>
        /// <returns><c>True</c> if the specified service has been release; otherwise, <c>false</c>.</returns>
        public virtual bool ReleaseAll(Type service, bool needDispose)
        {
            EnsureIsNotDisposed();
            Validate.ArgumentNotNull(service, "service");
            bool result = false;
            IList<IBinding> bindings = _componentContainer.BindingManagerComponent.FindAllBinding(service);
            for (int index = 0; index < bindings.Count; index++)
            {
                IBinding binding = bindings[index];
                binding.ScopeLifecycle.ReleaseObjects(needDispose);
                result = true;
            }
            return result;
        }

        #endregion

        #region Implementation of IServiceProvider

        /// <summary>
        /// Gets the service object of the specified type.
        /// </summary>
        /// <returns>
        /// A service object of type <paramref name="serviceType"/>.
        ///                     -or- 
        ///                 null if there is no service object of type <paramref name="serviceType"/>.
        /// </returns>
        /// <param name="serviceType">An object that specifies the type of service object to get. 
        ///                 </param>
        object IServiceProvider.GetService(Type serviceType)
        {
            return
                Resolve(new BindingContext(serviceType, null, null, this, this,
                                           InjectorUtilsInternal.EmptyInjectionParams,
                                           InjectorUtilsInternal.ReadOnlyEmptyDictionary));
        }

        #endregion

        #region Equality members

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        /// <param name="other">An object to compare with this object.
        ///                 </param>
        public bool Equals(InjectorBase other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return _id == other._id;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>. 
        ///                 </param><exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.
        ///                 </exception>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != this.GetType()) return false;
            return Equals((InjectorBase)obj);
        }

        /// <summary>
        /// Serves as a hash function for a particular type. 
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return _id;
        }

        #endregion

        #region Overrides of DisposableObject

        /// <summary>
        /// Releases resources held by the object.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                //Clear in parent.
                if (_parent != null)
                {
                    _parent.RemoveChild(this);
                    _parent = null;
                }

                for (int index = 0; index < _childInjectors.Count; index++)
                {
                    IInjector injector = _childInjectors[index];
                    injector.Dispose();
                    _childInjectors.Remove(injector);
                    index--;
                }
                lock (_injectorModules)
                {
                    for (int index = 0; index < _injectorModules.Count; index++)
                    {
                        IInjectorModule injectorModule = _injectorModules[index];
                        injectorModule.Unload(this);
                        _injectorModules.Remove(injectorModule);
                        index--;
                    }
                }
                _bindingCollection.Dispose();
                _componentContainer.Dispose();
            }
            base.Dispose(disposing);
        }

        #endregion
    }
}